﻿using RpcCacheClient.Helper;
using RpcClient.Interface;
using RpcHelper;
using RpcHelper.Error;
using RpcModel;
using RpcModel.Tran.Model;
using RpcSync.Collect;
using RpcSync.Model;
using RpcSync.Service.Interface;

namespace RpcSync.Service.Tran
{
    [RpcClient.Attr.ClassLifetimeAttr(RpcClient.Attr.ClassLifetimeType.单例)]
    internal class SingleTranService : ISingleTranService
    {
        private readonly IDataQueueHelper<Guid> _Queue = null;
        private IUnityCollect _Unity;
        private ITranConfig _Config;
        private IRemoteSendService _Send;
        public SingleTranService(IUnityCollect unity,
            IRemoteSendService send,
            ITranConfig config)
        {
            this._Send = send;
            this._Unity = unity;
            this._Config = config;
            string name = string.Concat("SingleTran_", RpcClient.RpcClient.CurrentSource.RegionId);
            this._Queue = new RedisDataQueue<Guid>(name, _ExecTran);
        }

        private void _ExecTran(Guid tranId)
        {
            ITransactionCollect tranCollect = _Unity.Resolve<ITransactionCollect>();
            TransactionDatum tran = tranCollect.GetTransaction(tranId);
            if (tran.TranStatus == TransactionStatus.待回滚 || tran.TranStatus == TransactionStatus.回滚失败)
            {
                _Rollback(tran, tranCollect);
            }
            else if (tran.TranStatus == TransactionStatus.已提交 &&
                tran.TranMode == RpcTranMode.Tcc &&
                (tran.CommitStatus == TranCommitStatus.待提交 || tran.CommitStatus == TranCommitStatus.提交失败))
            {
                _Commit(tran, tranCollect);
            }
        }
        private void _Commit(TransactionDatum tran, ITransactionCollect tranCollect)
        {
            if (_SendDicate<TranCommit>(tran, "Rpc_TranCommit", new TranCommit
            {
                TranId = tran.ParentId == Guid.Empty ? tran.Id : tran.ParentId,
                TranName = tran.TranName,
                SubmitJson = tran.SubmitJson,
                Extend = tran.Extend
            }))
            {
                tran.TranStatus = TransactionStatus.已提交;
            }
            else if (tran.RetryNum >= _Config.TranCommitRetryNum)
            {
                tran.CommitStatus = TranCommitStatus.提交错误;
            }
            else
            {
                tran.CommitStatus = TranCommitStatus.提交失败;
            }
            tranCollect.CommitResult(tran);
        }
        private bool _SendDicate<T>(TransactionDatum tran, string dicate, T data) where T : class
        {
            IRemoteConfig config = new IRemoteConfig(dicate, tran.SystemType, true, true)
            {
                RpcMerId = tran.RpcMerId,
                RegionId = tran.RegionId
            };
            if (_Send.Send<T>(config, data, out string error))
            {
                return true;
            }
            else
            {
                tran.RetryNum += 1;
                tran.ErrorId = LocalErrorManage.GetErrorId(error);
                return false;
            }
        }
        private void _Rollback(TransactionDatum tran, ITransactionCollect tranCollect)
        {
            if (tran.TranMode == RpcTranMode.NoReg)
            {
                tran.TranStatus = TransactionStatus.已回滚;
            }
            else if (_SendDicate<TranRollback>(tran, "Rpc_TranRollback", new TranRollback
            {
                TranId = tran.ParentId == Guid.Empty ? tran.Id : tran.ParentId,
                TranName = tran.TranName,
                SubmitJson = tran.SubmitJson,
                Extend = tran.Extend
            }))
            {
                tran.TranStatus = TransactionStatus.已回滚;
            }
            else if (tran.RetryNum >= _Config.TranRollbackRetryNum)
            {
                tran.TranStatus = TransactionStatus.回滚错误;
            }
            else
            {
                tran.TranStatus = TransactionStatus.回滚失败;
            }
            tranCollect.RollbackResult(tran);
        }

        public void AddQueue(Guid[] tranId)
        {
            _Queue.AddQueue(tranId);
        }
    }
}
